package com.tucc.hello.juc;

import cn.hutool.core.thread.RejectPolicy;
import lombok.SneakyThrows;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @author tucc
 * @description todo
 * @package com.tucc.hello.juc
 * @date 2021/5/26
 **/
public class TakeTurns {

    volatile int num = 1;

    final int times = 10;

    int threadNums;

    TakeTurns(int threadNums){
        this.threadNums = threadNums;
    }


    public static void main(String[] args) {
        TakeTurns takeTurns = new TakeTurns(4);
        takeTurns.startTasks();
    }

    public void startTasks(){
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10);
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(threadNums, threadNums,
                1L, TimeUnit.MINUTES, queue, RejectPolicy.DISCARD.getValue());
        ReentrantLock lock = new ReentrantLock();
        Condition[] conditions = new Condition[threadNums];
        for(int i = 0;i < threadNums;i ++){
            conditions[i] = lock.newCondition();
        }
        for(int i = 0;i < threadNums;i ++){
            int next = i == threadNums - 1 ? 0 : (i + 1);
            Task task = new Task(lock, conditions[i], conditions[next], i + 1,
                    this::changeNum, this::currentNum);
            threadPoolExecutor.submit(task);
        }
        threadPoolExecutor.shutdown();
    }

    Integer changeNum(){
        return num = (num) % threadNums + 1;
    }

    Integer currentNum(){
        return num;
    }

    class Task implements Runnable{

        int myNum;

        Condition myCondition;

        Condition nextCondition;

        ReentrantLock lock;

        Supplier<Integer> stateChange;

        Supplier<Integer> currentState;

        public Task(ReentrantLock lock, Condition condition, Condition nextCondition, int target,
                    Supplier<Integer> stateChange, Supplier<Integer> currentState){
            this.lock = lock;
            this.myCondition = condition;
            this.nextCondition = nextCondition;
            this.myNum = target;
            this.stateChange = stateChange;
            this.currentState = currentState;
        }

        boolean isMyTurn(){
            return myNum == currentState.get();
        }

        @SneakyThrows
        @Override
        public void run() {
            int curTime = 0;
            while(curTime ++ < times){
                lock.lock();
                try {
                    int curNum = currentState.get();
                    if (curNum != myNum) {
                        myCondition.await();
                    }
                    System.out.println(String.format("times %d, print %d", curTime, myNum));
                    stateChange.get();
                    nextCondition.signal();
                }finally {
                    lock.unlock();
                }
            }
        }
    }
}
